/**
 * 
 */
package ro.dta.idbi.ui.patterns;

import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.swing.JLabel;

import ro.dta.idbi.interfaces.sql.ISqlColumn;
import ro.dta.idbi.interfaces.sql.ISqlConstruct;
import ro.dta.idbi.interfaces.sql.ISqlForeignKey;
import ro.dta.idbi.interfaces.sql.ISqlTable;
import ro.dta.idbi.interfaces.ui.IMainFrame;
import ro.dta.idbi.patterns.TableGener;
import ro.dta.idbi.ui.sql.SqlForeignKeyCell;
import ro.dta.idbi.ui.sql.SqlGraph;
import ro.dta.idbi.ui.sql.SqlTableCell;
import uk.ac.ic.doc.automed.reps.Schema;

import com.mxgraph.layout.hierarchical.mxHierarchicalLayout;
import com.mxgraph.model.mxGeometry;
import com.mxgraph.swing.mxGraphComponent;
import com.mxgraph.util.mxEvent;
import com.mxgraph.util.mxEventObject;
import com.mxgraph.util.mxEventSource.mxIEventListener;

/**
 * UI for the introduction of total generalisation pattern
 * 
 * @author Tudor Dobrila
 * 
 */
public class TableGenerDialog extends AbstractPatternDialog {

	private static final long serialVersionUID = 967501030119870916L;

	private static final Logger LOGGER = Logger
			.getLogger("ro.dta.idbi.ui.patterns.TableGenerDialog");
	
	/**
	 * Check if the current pattern can be applied over a given set of objects
	 * 
	 * Used to check if the items selected by the user in the UI can be used to
	 * apply this transformation pattern over them
	 * 
	 * @param schema
	 *            Schema in the context of which to check the validity of the
	 *            array of objects
	 * @param objects
	 *            Set of objects
	 * @return True if pattern can be applied over set of objects, false
	 *         otherwise
	 */
	public static boolean isValid(final Schema schema, final Object[] objects) {
		if (objects.length <= 1) {
			return false;
		}

		for (Object o : objects) {
			if (!(o instanceof ISqlTable)) {
				return false;
			}
		}

		// Check if all tables have the same PK
		ArrayList<String> primaryKeys = null;
		for (Object o : objects) {
			final ISqlTable crtTbl = (ISqlTable) o;
			final List<ISqlColumn> cols = crtTbl.getColumns();
			if (primaryKeys == null) {
				primaryKeys = new ArrayList<String>();
				for (ISqlColumn c : cols) {
					if (c.isPK()) {
						primaryKeys.add(c.getValue().toString());
					}
				}
			} else {
				for (ISqlColumn c : cols) {
					if (c.isPK() && !primaryKeys.contains(c.getValue().toString())) {
						return false;
					}
				}
			}
		}

		return true;
	}

	private transient ISqlForeignKey[] fks;

	private transient SqlTableCell newTable;

	/**
	 * Introduction of total generalisation dialog class constructor
	 * 
	 * @param parentFrame
	 *            Parent frame of this dialog
	 * @param schema
	 *            Schema over which the transformation is applied
	 * @param element
	 *            Sql construct(s) over which the transformation is applied
	 * @param beforeLabel
	 *            Label for the before diagram
	 * @param afterLabel
	 *            Label for the transformed diagram
	 */
	public TableGenerDialog(final IMainFrame parentFrame, final Schema schema,
			final ISqlConstruct[] element, final JLabel beforeLabel, final JLabel afterLabel) {
		super(parentFrame, schema, element, beforeLabel, afterLabel);

		if (!isValid(schema, element)) {
			throw new IllegalArgumentException(
					"This pattern can't be applied to the given elements");
		}

		updateMacro("newTable");
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see ro.dta.idbi.ui.patterns.TransformationDialog#createOriginalDiagram()
	 */
	@Override
	protected mxGraphComponent createOriginalDiagram() {
		final SqlGraph graph = new SqlGraph();
		graph.getModel().beginUpdate();

		try {
			for (int i = 0; i < element.length; i++) {
				final SqlTableCell crtTbl = (SqlTableCell) element[i];
				final SqlTableCell newTable = (SqlTableCell) crtTbl.clone();
				final mxGeometry newGeometry = new mxGeometry(i * 170 + 5, 5, element[i]
						.getGeometry().getWidth(), element[i].getGeometry().getHeight());
				newTable.setGeometry(newGeometry);
				newTable.layout(graph);
			}
		} catch (CloneNotSupportedException e) {
			LOGGER.log(Level.SEVERE, "Fatal exception", e);
		} finally {
			graph.setAutoSizeCells(true);
			graph.getModel().endUpdate();
		}

		return new mxGraphComponent(graph);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * ro.dta.idbi.ui.patterns.TransformationDialog#createTransformedDiagram()
	 */
	@Override
	protected mxGraphComponent createTransformedDiagram() {
		final SqlGraph graph = new EditableGraph();
		graph.getModel().beginUpdate();

		try {
			// Clone original tables
			SqlTableCell[] origTables = new SqlTableCell[element.length];
			for (int i = 0; i < element.length; i++) {
				final SqlTableCell crtTbl = (SqlTableCell) element[i];
				origTables[i] = (SqlTableCell) crtTbl.clone();
				final mxGeometry newGeometry = new mxGeometry(i * 170 + 5, 5, element[i]
						.getGeometry().getWidth(), element[i].getGeometry().getHeight());
				origTables[i].setGeometry(newGeometry);
				origTables[i].layout(graph);
			}

			// Add new table
			final mxGeometry newGeometry = new mxGeometry(5, 5,
					element[0].getGeometry().getWidth(), element[0].getGeometry().getHeight());
			newTable = new SqlTableCell(null, null, "", "newTable", newGeometry, "SEL_TABLE");

			// Add PK columns to the new table
			for (ISqlColumn col : origTables[0].getColumns()) {
				if (col.getStyle().equals("PRIMARYKEY")) {
					final ISqlColumn newCol = (ISqlColumn) col.clone();
					newTable.addColumn(newCol);
				}
			}
			newTable.relayout(graph);

			fks = new ISqlForeignKey[element.length];
			for (int i = 0; i < element.length; i++) {
				final String fkName = origTables[i].getValue() + "_isa_newTable_fk";
				fks[i] = new SqlForeignKeyCell(null, null, "", fkName, origTables[i], newTable,
						"FK_EDGE");
				graph.addCell(fks[i], graph.getDefaultParent());
			}
		} catch (CloneNotSupportedException e) {
			LOGGER.log(Level.SEVERE, "Fatal exception", e);
		} finally {
			final mxHierarchicalLayout layout = new mxHierarchicalLayout(graph);
			layout.execute(graph.getDefaultParent());
			graph.getModel().endUpdate();
		}

		final mxGraphComponent graphComponent = new mxGraphComponent(graph);
		graphComponent.setEnterStopsCellEditing(true);
		graphComponent.addListener(mxEvent.LABEL_CHANGED, new mxIEventListener() {

			@Override
			public void invoke(final Object arg0, final mxEventObject arg1) {
				final Object cell = arg1.getProperty("cell");
				final String newName = ((ISqlTable) cell).getValue().toString();

				for (int i = 0; i < fks.length; i++) {
					final String fkName = element[i].getValue() + "_isa_" + newName + "_fk";
					fks[i].setValue(fkName);
				}

				graphComponent.revalidate();
				graphComponent.repaint();

				updateMacro(newName);
			}
		});

		graphComponent.startEditingAtCell(newTable);

		return graphComponent;
	}

	private void updateMacro(final String tableName) {
		final StringBuilder builder = new StringBuilder(TableGener.COMMAND).append(" (<<");
		builder.append(tableName).append(">>, ");
		for (int i = 0; i < element.length; i++) {
			builder.append("<<").append(element[i].getValue()).append(">>, ");
		}
		builder.setLength(builder.length() - 2);
		builder.append(")");
		macroField.setText(builder.toString());
	}

}
